﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;

using Xpus.Website.Utilities;
using Xpus.Website.Data;
using Xpus.Website.Configuration;

namespace Xpus.Website.Core.Membership
{
	public static class XpusMembership
	{
		/// <summary>
		/// 用于对密码进行散列的算法名字。
		/// </summary>
		public const string PasswordHashAlgorithmName = "SHA1";

		/// <summary>
		/// 使用给定的Salt值对密码进行散列。
		/// </summary>
		/// <param name="password">密码。</param>
		/// <param name="salt">Salt值。</param>
		/// <returns>经过散列的密码。</returns>
		static string EncodePassword(string password, string salt)
		{
			byte[] src = Encoding.Unicode.GetBytes(password);
			byte[] saltbuf = Convert.FromBase64String(salt);
			byte[] dst = new byte[saltbuf.Length + src.Length];
			byte[] inArray = null;
			Buffer.BlockCopy(saltbuf, 0, dst, 0, saltbuf.Length);
			Buffer.BlockCopy(src, 0, dst, saltbuf.Length, src.Length);

			HashAlgorithm algorithm = HashAlgorithm.Create(PasswordHashAlgorithmName);
			if(algorithm == null)
				throw new Exception(string.Format(LanguageUtility.GetString("Hash algorithm '{0}' could not be found."), PasswordHashAlgorithmName));

			inArray = algorithm.ComputeHash(dst);

			return Convert.ToBase64String(inArray);
		}

		/// <summary>
		/// 生成随机的Salt值。
		/// </summary>
		/// <returns>表示Salt值的base64字符串。</returns>
		static string GenerateSalt()
		{
			byte[] data = new byte[0x10];
			new RNGCryptoServiceProvider().GetBytes(data);
			return Convert.ToBase64String(data);
		}

		/// <summary>
		/// 创建一个用户。
		/// </summary>
		/// <param name="userName">用户名。</param>
		/// <param name="password">密码。</param>
		/// <param name="nickName">昵称。</param>
		/// <param name="securityEmail">安全邮箱。</param>
		/// <param name="passwordQuestion">密码提示问题。</param>
		/// <param name="passwordAnswer">密码提示问题答案。</param>
		/// <param name="registerIP">用户注册时的IP地址。</param>
		/// <param name="contactEmail">联系邮箱。</param>
		/// <returns>如果创建成功，返回<see cref="User"/>对象；否则返回null。</returns>
		public static User CreateUser(
			string userName,
			string password,
			string nickname,
			string securityEmail,
			string passwordQuestion,
			string passwordAnswer,
			string registerIP,
			string contactEmail)
		{
			if(User.IsUserNameExists(userName))
				throw new UserExistsException(string.Format(LanguageUtility.GetString("User '{0}' is already exists."), userName));

			if(User.IsSecurityEmailExists(securityEmail))
				throw new UserExistsException(string.Format(LanguageUtility.GetString("Security email '{0}' has been used."), securityEmail));
			User newUser = new User();

			newUser.UserId = Guid.NewGuid();
			newUser.UserName = userName;
			newUser.LoweredUserName = userName.ToLower();
			newUser.Nickname = nickname;
			newUser.SecurityEmail = securityEmail;
			newUser.LoweredSecurityEmail = securityEmail.ToLower();
			newUser.PasswordQuestion = passwordQuestion;
			newUser.PasswordAnswer = passwordAnswer;
			newUser.PasswordChangeVerificationCode = Guid.Empty;
			newUser.CreatedTime = DateTime.Now;
			newUser.RegisterIP = registerIP;
			newUser.IsLockOut = false;
			newUser.LastLoginTime = newUser.CreatedTime;
			newUser.LastPasswordChangedTime = newUser.CreatedTime;
			newUser.ContactEmail = contactEmail;

			string salt = GenerateSalt();
			string pwdhash = EncodePassword(password, salt);
			newUser.PasswordHash = pwdhash;
			newUser.PasswordSalt = salt;

			XpusDataContext dc = new XpusDataContext(XpusConfigurationSection.Instance.Database.ConnectionString);
			dc.Users.Add(newUser);

			dc.SubmitChanges();

			return newUser;
		}

		/// <summary>
		/// 判断用户凭证是否有效。
		/// </summary>
		/// <param name="userNameOrEmail">用户名或安全邮箱。</param>
		/// <param name="password">密码。</param>
		/// <returns>密码是否正确。</returns>
		public static bool CheckUser(string userNameOrEmail, string password)
		{
			User u = null;

			if(userNameOrEmail.IsEmailAddress())
				u = User.GetBySecurityEmail(userNameOrEmail);
			else
				u = User.GetByUserName(userNameOrEmail);

			if(u == null)
				return false;

			string encodedPassword = EncodePassword(password, u.PasswordSalt);
			return encodedPassword == u.PasswordHash;
		}
	}
}
